package MaquinaVirtual;

import java.io.Serializable;
import MaquinaVirtual.TinyVM.IntPValue;


abstract public class Insts implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	public enum ICOD {SUM, MUL,APILA,PROG_VACIO,COPIA,
		EQ,NEQ,GT,GE,LT,LE,RESTA,OR,DIV,AND,MENOS,NOT,IR_F,IR_A,DESAPILA,COD_INICIO,RESERVA,LIBERA,RET,
		INICIO_PASO, MUEVE, DESAPILA_DIR, APILA_IND, APILA_DIR, DESAPILA_IND, IR_IND}
    
	
	private static IIr_a iira= null;
	private static IIr_f iirf= null;
	private static IIr_Ind iirind = null;
	private static ICopia icopia = null;
	private static IApila iapila = null;
	private static IApilaDir iapiladir= null;
	private static IApilaInd iapilaind = null;
	private static IDesapila idesapila = null;
	private static IDesapilaDir idesapiladir= null;
	private static IDesapilaInd idesapilaind = null;
	private static IOr ior = null;
	private static IMul imul = null;
	private static IDiv idiv = null;
	private static IAnd iand = null;
	private static IMenos imenos = null;
	private static INot inot = null;		
	private static IEq ieq = null;
	private static INeq ineq = null;
	private static IMayor igt = null;
	private static IMayorIgual ige = null;
	private static IMenor ilt = null;
	private static IMenorIgual ile = null;
	private static IResta iresta = null;
	private static ISuma isuma = null;
	private static IInicio iinicio = null;
	private static ICodigoRet iret = null;
	private static IInicioPaso iiniciopaso= null;
	private static IMueve imueve=null;
	private static IEscritura iescritura =null;
	
	
    abstract public ICOD ci();
    abstract public void ejecuta(TinyVM vm);

    public static class ICopia extends Insts {
		private static final long serialVersionUID = 1L;

		private ICopia() {    
        }  
    	
		public ICOD ci() {return ICOD.COPIA;}
		
		public String toString() {
   			return "copia()";
   		}

		public void ejecuta(TinyVM vm) {
			TinyVM.PValue cima= vm.peek();			
			if (cima instanceof TinyVM.IntPValue){
				TinyVM.PValue copia= new TinyVM.IntPValue(cima.asInt());
				vm.push(copia);	
				vm.incCP();
			}
		}
    }
	
    public static class IApila extends Insts {
		private static final long serialVersionUID = 1L;
		private IApila(Integer val) {
           try {
             this.val = val;    
           }
           catch(NumberFormatException e) {
             this.val = 0;   
           }
        }   
        public ICOD ci () {return ICOD.APILA;}
        public String toString() {return "APILA("+val+")";}
        public void ejecuta(TinyVM vm) {
        	 vm.push(new TinyVM.IntPValue(val));
             vm.incCP();
        }
        private Integer val;
     
      }
    
    public static class IApilaInd extends Insts {

    	private static final long serialVersionUID = 1L;
		private IApilaInd() {    
        }  
    	
		public ICOD ci() {return ICOD.APILA_IND;}
		
		public String toString() {
   			return "apila_ind()";
   		}

		public void ejecuta(TinyVM vm) {
			pos = vm.pop().asInt();
			val= vm.getMem(pos);
			vm.push(val);
			vm.incCP();
		}
		private Integer pos;
		private TinyVM.PValue val;
    }
    
    public static class IApilaDir extends Insts {
		private static final long serialVersionUID = 1L;
		private IApilaDir(Integer dir) {
		   this.dir =dir;    
	    }   
	    public ICOD ci () {return ICOD.APILA_DIR;}
	    public String toString() {return "APILA_DIR("+String.valueOf(dir)+")";}
	    public void ejecuta(TinyVM vm) {
	    	 val= vm.getMem(dir);  
	         vm.push(val);
	         vm.incCP();
	    }
	    private Integer dir;
	    private TinyVM.PValue val;
	    
	}
    
    
    public static class IDesapila extends Insts {
		private static final long serialVersionUID = 1L;

		public IDesapila(){}
    	
		public ICOD ci() {return ICOD.DESAPILA;}
		
		public String toString() {
   			return "desapila()";
   		}

		public void ejecuta(TinyVM vm) {
			vm.pop();
			vm.incCP();
		}
    }
    
    public static class IDesapilaDir extends Insts {

		private static final long serialVersionUID = 1L;
		public IDesapilaDir(Integer dir){
	            this.dir =dir;    
    	}
    	
		public ICOD ci() {return ICOD.DESAPILA_DIR;}
		
		public String toString() {
   			return "desapila_dir(" + String.valueOf(dir) + ")";
   		}

		public void ejecuta(TinyVM vm) {
			val=vm.pop();
			vm.setMem(dir, val);
			vm.incCP();
		}
		
	       private Integer dir;
	       private TinyVM.PValue val;
    }
    
    public static class IDesapilaInd extends Insts {
		private static final long serialVersionUID = 1L;
		private IDesapilaInd() {    
        } 
   
		public ICOD ci() {return ICOD.DESAPILA_IND;}
		
		public String toString() {
   			return "desapila_ind()";
   		}

		public void ejecuta(TinyVM vm) {
			val = vm.pop();
			pos = vm.pop().asInt();
			vm.setMem(pos, val);
			vm.incCP();
		}
		private TinyVM.PValue val;
		private Integer pos;
    }
 
    public static class IOr extends Insts {
		private static final long serialVersionUID = 1L;
		private IOr() {
   		}
   		public ICOD ci() {
   			return ICOD.OR;
   		}
   		public String toString() {
   			return "OR()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
			TinyVM.PValue op1 = vm.pop();
			if(op2.asInt()==0 && op1.asInt()==0){
				vm.push(new TinyVM.IntPValue(0));
			}else{
				vm.push(new TinyVM.IntPValue(1));
			}
			vm.incCP();
		}
   	}

    public static class IMul extends Insts {
		private static final long serialVersionUID = 1L;
		private IMul() {} 
           public ICOD ci () {return ICOD.MUL;}
           public String toString() {return "MUL()";}
           public void ejecuta(TinyVM vm) {
        	   TinyVM.PValue op2 = vm.pop();
               TinyVM.PValue op1 = vm.pop();
               vm.push(new TinyVM.IntPValue(op1.asInt()*op2.asInt()));
               vm.incCP();
           }
    }
   	
   	public static class IDiv extends Insts {
		private static final long serialVersionUID = 1L;
		private IDiv() {
   		}
   		public ICOD ci() {
   			return ICOD.DIV;
   		}
   		public String toString() {
   			return "DIV()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
	        TinyVM.PValue op1 = vm.pop();
	        vm.push(new TinyVM.IntPValue(op1.asInt()/op2.asInt()));
	        vm.incCP();
		}
   	}
   	
   	public static class IAnd extends Insts {
		private static final long serialVersionUID = 1L;
		private IAnd() {
   		}
   		public ICOD ci() {
   			return ICOD.AND;
   		}
   		public String toString() {
   			return "AND()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
			TinyVM.PValue op1 = vm.pop();
			if(op2.asInt()==0 || op1.asInt()==0){
				vm.push(new TinyVM.IntPValue(0));
			}
			else{
				vm.push(new TinyVM.IntPValue(1));
			}
			vm.incCP();
		}
   	}
   	
   	public static class IMenos extends Insts {
		private static final long serialVersionUID = 1L;
		private IMenos() {
   		}
   		public ICOD ci() {
   			return ICOD.MENOS;
   		}
   		public String toString() {
   			return "MENOS()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op1 = vm.pop();
			vm.push(new TinyVM.IntPValue(-op1.asInt()));
			vm.incCP();
		}
   	}
   	
   	public static class INot extends Insts {
		private static final long serialVersionUID = 1L;
		private INot() {
   		}
   		public ICOD ci() {
   			return ICOD.NOT;
   		}
   		public String toString() {
   			return "NOT()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op1 = vm.pop();
			if(op1.asInt()==0){
				vm.push(new TinyVM.IntPValue(1));
			}
			else {
				vm.push(new TinyVM.IntPValue(0));
			}
			vm.incCP();
		}
   	}
   	
   	
   	public static class IEq extends Insts {
		private static final long serialVersionUID = 1L;
		private IEq() {
   		}
   		public ICOD ci() {
   			return ICOD.EQ;
   		}
   		public String toString() {
   			return "EQ()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
			TinyVM.PValue op1 = vm.pop();
			if(op1.asInt()==op2.asInt()){
				vm.push(new TinyVM.IntPValue(1));
			}
			else {
				vm.push(new TinyVM.IntPValue(0));
			}
			vm.incCP();
		}
   	}
   	
   	public static class INeq extends Insts {
		private static final long serialVersionUID = 1L;
		private INeq() {
   		}
   		public ICOD ci() {
   			return ICOD.NEQ;
   		}
   		public String toString() {
   			return "NEQ()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
			TinyVM.PValue op1 = vm.pop();
			if(op1.asInt()!=op2.asInt()){
				vm.push(new TinyVM.IntPValue(1));
			}
			else {
				vm.push(new TinyVM.IntPValue(0));
			}
			vm.incCP();
		}
   	}
   	
   	public static class IMayor extends Insts {
		private static final long serialVersionUID = 1L;
		private IMayor() {
   		}
   		public ICOD ci() {
   			return ICOD.GT;
   		}
   		public String toString() {
   			return "GT()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
			TinyVM.PValue op1 = vm.pop();
			if(op1.asInt()>op2.asInt()){
				vm.push(new TinyVM.IntPValue(1));
			}
			else {
				vm.push(new TinyVM.IntPValue(0));
			}
			vm.incCP();
		}
   	}
   	
   	public static class IMayorIgual extends Insts {
		private static final long serialVersionUID = 1L;
		private IMayorIgual() {
   		}
   		public ICOD ci() {
   			return ICOD.GE;
   		}
   		public String toString() {
   			return "GE()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
			TinyVM.PValue op1 = vm.pop();
			if(op1.asInt()>=op2.asInt()){
				vm.push(new TinyVM.IntPValue(1));
			}
			else {
				vm.push(new TinyVM.IntPValue(0));
			}
			vm.incCP();
		}
   	}
   	
   	public static class IMenor extends Insts {
		private static final long serialVersionUID = 1L;
		private IMenor() {
   		}
   		public ICOD ci() {
   			return ICOD.LT;
   		}
   		public String toString() {
   			return "LT()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
			TinyVM.PValue op1 = vm.pop();
			if(op1.asInt()<op2.asInt()){
				vm.push(new TinyVM.IntPValue(1));
			}
			else {
				vm.push(new TinyVM.IntPValue(0));
			}
			vm.incCP();
		}
   	}
   	
   	public static class IMenorIgual extends Insts {
		private static final long serialVersionUID = 1L;
		private IMenorIgual() {
   		}
   		public ICOD ci() {
   			return ICOD.LE;
   		}
   		public String toString() {
   			return "LE()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
			TinyVM.PValue op1 = vm.pop();
			if(op1.asInt()<=op2.asInt()){
				vm.push(new TinyVM.IntPValue(1));
			}
			else {
				vm.push(new TinyVM.IntPValue(0));
			}
			vm.incCP();
		}
   	}
   	
   	public static class IInicio extends Insts {
		private static final long serialVersionUID = 1L;
		private IInicio(Integer numNiveles, Integer tam){
    		this.tam = tam;
    		this.numNiveles = numNiveles;
    	}
		@Override
		public ICOD ci() {
	    
			return null;
		}
		
		public String toString() {
   			return "INICIO";
   		}

		@Override
		public void ejecuta(TinyVM vm) {
			int cp = vm.getCp();
			IApila inst1 = new IApila(numNiveles+2);
			inst1.ejecuta(vm);	
			IDesapilaDir inst2= new IDesapilaDir(1);
			inst2.ejecuta(vm);
			IApila inst3 = new IApila(numNiveles+tam+1); //TODO revisar: +1?
			inst3.ejecuta(vm);
			IDesapilaDir inst4= new IDesapilaDir(0);
			inst4.ejecuta(vm);
			vm.setCp(cp+1);
		}
		private int tam;
		private int numNiveles;
    	
    }
   	
   	
   	
	public static class IIr_a extends Insts {
		private static final long serialVersionUID = 1L;
		private Integer etq;
   		private IIr_a(Integer etq) {
   				this.etq = etq;	
   		}
   		public ICOD ci() {
   			return ICOD.IR_A;
   		}
   		public void setEtq(int nueva){
   			this.etq = nueva;
   		}
   
   		public String toString() {
   			return "IR_A(" + etq + ")";
   		}
		public void ejecuta(TinyVM vm) {
			vm.setCp(etq-1);
		}	
   	}
    
	public static class IIr_f extends Insts {
		private static final long serialVersionUID = 1L;
		private Integer etq;
   		private IIr_f(Integer etq) {
   		     this.etq =etq;		
   		}

   		public ICOD ci() {
   			return ICOD.IR_F;
   		}
 
   		public String toString() {
   			return "IR_F(" + etq + ")";
   		}

		public void ejecuta(TinyVM vm) {
			TinyVM.IntPValue valor;
			
			IApilaInd inst2= new IApilaInd();
			inst2.ejecuta(vm);
			valor = (TinyVM.IntPValue) vm.pop();
			if (valor.asInt() == 0) {
				vm.setCp(etq);
			} else
				vm.incCP();
		}	
   	}
	
	public static class IIr_Ind extends Insts {
		private static final long serialVersionUID = 1L;
   		private IIr_Ind() {
   		}

   		public ICOD ci() {
   			return ICOD.IR_IND;
   		}
 
   		public String toString() {
   			return "IR_IND";
   		}

		public void ejecuta(TinyVM vm) {
			TinyVM.IntPValue dir = (IntPValue) vm.pop();
			vm.setCp(dir.asInt());
		}	
   	}
   	
   	public static class IResta extends Insts {
		private static final long serialVersionUID = 1L;
		private IResta() {
   		}
   		public ICOD ci() {
   			return ICOD.RESTA;
   		}
   		public String toString() {
   			return "resta()";
   		}
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue op2 = vm.pop();
	        TinyVM.PValue op1 = vm.pop();
	        vm.push(new TinyVM.IntPValue(op1.asInt()-op2.asInt()));
	        vm.incCP();
		}
   	}

    public static class ISuma extends Insts {
		private static final long serialVersionUID = 1L;
		private ISuma() {} 
        public ICOD ci () {return ICOD.SUM;}
        public String toString() {return "SUMA";}
        public void ejecuta(TinyVM vm) {
        	TinyVM.PValue op2 = vm.pop();
	        TinyVM.PValue op1 = vm.pop();
	        vm.push(new TinyVM.IntPValue(op1.asInt()+op2.asInt()));
	        vm.incCP();
        }
     }
    
    public static class IInicioPaso extends Insts {
		private static final long serialVersionUID = 1L;
		private IInicioPaso() {}
    	@Override
		public ICOD ci() {
			return ICOD.INICIO_PASO;
		}
    	public String toString() {return "INICIO_PASO";}
		@Override
		public void ejecuta(TinyVM vm) {
			int cp = vm.getCp();
			IApilaDir iapiladir = new IApilaDir(0);
			IApila iapila = new IApila(3);
			ISuma isuma = new ISuma();
			iapiladir.ejecuta(vm);
			iapila.ejecuta(vm);
			isuma.ejecuta(vm);
			vm.setCp(cp + 1);
		}
    	
    }
    
    public static class IMueve extends Insts {
		private static final long serialVersionUID = 1L;

		private IMueve(){}
    	
    	public String toString() {return "MUEVE";}
		@Override
		public ICOD ci() {
			
			return ICOD.MUEVE;
		}

		@Override
		public void ejecuta(TinyVM vm) {
			TinyVM.PValue cima = vm.pop();
			TinyVM.PValue subcima = vm.pop();
			vm.setMem(subcima.asInt(), vm.getMem(cima.asInt()));
		}
    
    }
    
    public static class ICodigoRet extends Insts {

		private static final long serialVersionUID = 1L;

		private ICodigoRet(Integer dirret) {this.dirret=dirret;}
    	public ICOD ci () {return ICOD.RET;}
        public String toString() {return "apila-ret("+ dirret.intValue() +")";}
        public void ejecuta(TinyVM vm) {
        	int cp = vm.getCp();
        	IApilaDir inst1 = new IApilaDir(0);
    		inst1.ejecuta(vm);
    		IApila inst2 = new IApila(1);
    		inst2.ejecuta(vm);
    		ISuma inst3 = new ISuma();
    		inst3.ejecuta(vm);
    		IApila inst4 = new IApila(dirret);
    		inst4.ejecuta(vm);
    		IDesapilaInd inst5 = new IDesapilaInd();
    		inst5.ejecuta(vm);
    		vm.setCp(cp+1);

        }
		private Integer dirret;
    }
    
    public static class IEscritura extends Insts {

		private static final long serialVersionUID = 1L;

		private IEscritura() {}
    	public ICOD ci () {return ICOD.RET;}
        public String toString() {return "escribe";}
        public void ejecuta(TinyVM vm) {
        	int cp = vm.getCp();
        	IApilaInd inst = new IApilaInd();
        	inst.ejecuta(vm);
        	IntPValue valor = (IntPValue) vm.pop();
        	System.out.println(String.valueOf(valor.asInt()));
        	vm.setCp(cp+1);
        }
    }

   
    
    public static Insts nuevaIIra(Integer val) {
		
		iira = new IIr_a(val);
		return iira;
	}
 	
 	
 	
    public static Insts nuevaIIrf(Integer val) {
		
		iirf = new IIr_f(val);
		return iirf;
	}
    
    public static Insts nuevaCopia() {
     	
     	icopia = new ICopia();
     	 return icopia; 
      } 
 
     public static Insts nuevaIApila(Integer val) {
    	 
  		iapila = new IApila(val);
    	 return iapila; 
     }
     
  	public static Insts nuevaIDesapila() {
 		
 		idesapila = new IDesapila();
 		return idesapila;
 	}
  
     
     public static Insts nuevaIOr() {
 
 		ior = new IOr();
 		return ior;
 	}
 	
 	public static Insts nuevaIDiv() {
 		
 		idiv = new IDiv();
 		return idiv;
 	}
 	
 	public static Insts nuevaIAnd() {
 	
 		iand = new IAnd();
 		return iand;
 	}
 	
 	public static Insts nuevaIMenos() {
 	
 		imenos = new IMenos();
 		return imenos;
 	}
 	
 	public static Insts nuevaINot() {
 	
 		inot = new INot();
 		return inot;
 	}

 	public static Insts nuevaIEq() {
 		
 		ieq = new IEq();
 		return ieq;
 	}
 	
 	public static Insts nuevaINeq() {
 		
 		ineq = new INeq();
 		return ineq;
 	}
 	
 	public static Insts nuevaIMayor() {
 	
 		igt = new IMayor();
 		return igt;
 	}
 	
 	public static Insts nuevaIMayorIgual() {
 	
 		ige = new IMayorIgual();
 		return ige;
 	}
 	
 	public static Insts nuevaIMenor() {
 	
 		ilt = new IMenor();
 		return ilt;
 	}
 	
 	public static Insts nuevaIMenorIgual() {
 	
 		ile = new IMenorIgual();
 		return ile;
 	}
 	
 	public static Insts nuevaISuma() {
 	
 	    isuma = new ISuma();
 		return isuma;
 	}
 	
 	public static Insts nuevaIResta() {
 		
 		iresta = new IResta();
 		return iresta;
 	}

 	public static Insts nuevaIMul() {
 		
 		imul = new IMul();
 		return imul;
 	}
 	
 	public static Insts nuevaIInicio(Integer numNiveles, Integer tamDatos) {

		iinicio = new IInicio(numNiveles, tamDatos);
		return iinicio;
	}
 	
 	public static Insts nuevaIApilaInd() {
 		  
 	   	iapilaind = new IApilaInd();
 	   	 return iapilaind; 
 	    }
 	
 	public static Insts nuevaIDesapilaDir(Integer dir) {
    	
    	idesapiladir = new IDesapilaDir(dir);
    	 return idesapiladir; 
     }

 	public static Insts nuevaIApilaDir(Integer dir) {
	 
 	iapiladir = new IApilaDir(dir);
 	 return iapiladir; 
  }

 	public static Insts nuevaIDesapilaInd() {
		
		idesapilaind = new IDesapilaInd();
		return idesapilaind;
	}
 	
 	public static Insts nuevaInicioPaso() {
 	 	
 		iiniciopaso  = new IInicioPaso();
 		return iiniciopaso;
 	}
 	
 	public static Insts nuevaIMueve() {
 		
 		imueve  = new IMueve();
 		return imueve;
 	}
 	
 	public static Insts nuevaIRet(Integer ret) {
 		
 		iret = new ICodigoRet(ret);
 		return iret;
 	}
 	
 	public static Insts nuevaIIrInd() {
 		iirind = new IIr_Ind();
 		return iirind;
 	}
 	
 	public static Insts nuevaIEscritura(){
 		iescritura = new IEscritura();
 		return iescritura;
 	}

}

